package gao.xioalei.filter;

import gao.xioalei.dto.JwtDto;
import gao.xioalei.service.AuthVersionService;
import gao.xioalei.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.net.URI;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;

@Component
public class AuthFilter implements GlobalFilter, Ordered {
    @Autowired
    private AntPathMatcher antPathMatcher;

    @Autowired
    private RestTemplate restTemplate;

    @Resource(name = "AuthVersionService")
    private AuthVersionService authVersionService;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String requestUrl = request.getURI().getPath();//获取请求路径
        if (antPathMatcher.match("/favicon.ico", requestUrl) || antPathMatcher.match("/training-web-service/login", requestUrl) || antPathMatcher.match("/training-auth-manage-service/login", requestUrl) || antPathMatcher.match("/training-web-service/register", requestUrl) || antPathMatcher.match("/training-auth-manage-service/register", requestUrl) || antPathMatcher.match("/training-web-service/static/**", requestUrl) || (antPathMatcher.match("/training-image-manage-service/**", requestUrl) && "GET".equals(request.getMethod()) || (antPathMatcher.match("/testConfigFile", requestUrl))))//如果是登陆或者注册或者静态资源获取或者获取图片服务器图片则不拦截
            return chain.filter(exchange);
        String method = request.getMethodValue();
        if (antPathMatcher.match("/training-auth-manage-service/logout", requestUrl)) {//如果是退出登录则直接销毁Cookie即可
            ResponseCookie cookie = ResponseCookie.from("access_token", null).path("/").maxAge(0).build();
            exchange.getResponse().addCookie(cookie);
            exchange.getResponse().setStatusCode(HttpStatus.SEE_OTHER);//重定向状态码
            exchange.getResponse().getHeaders().setLocation(URI.create("/training-web-service/login"));//重定向到登陆页面
            return exchange.getResponse().setComplete();
        }
        MultiValueMap<String, HttpCookie> cookies = request.getCookies();
        HttpCookie tokenCookie = cookies.getFirst("access_token");
        String access_token = tokenCookie == null ? null : tokenCookie.getValue();
        if (JwtUtil.verify(access_token)) {
            showDetails(request, access_token);
            Integer version = JwtUtil.getVersion(access_token);
            String userId = JwtUtil.getUserId(access_token);
            if (!authVersionService.versionCheck(userId, version)) {//判断用户的权限有没有更新,更新了的话则刷新access_token
                JwtDto refreshToken = restTemplate.getForObject("http://training-auth-manage-service/refresh/".concat(userId), JwtDto.class);
                ResponseCookie cookie = ResponseCookie.from("access_token", refreshToken.getToken())
                        .path("/")//设置作用域,如果不设置cookie的path就是/user,那么去请求/user的父亲和兄弟路径就不会带上该cookie
                        .maxAge(24 * 60 * 60 * 1000)
                        .build();
                exchange.getResponse().addCookie(cookie);
            }
            String[] roles = JwtUtil.getRoles(access_token);//获取token的角色
            String[] auths = JwtUtil.getAuth(access_token);//获取token的权限点
            Map<String, String[]> transientAuth = null;
            if (JwtUtil.getTransientAuth(access_token) != null)
                transientAuth = JwtUtil.getTransientAuth(access_token);//获取token的临时权限点
            String auth = null;
            int readOnly, index;
            for (int i = 0, length = auths.length; i < length; i++) {
                if (auths[i] != null) {
                    index = auths[i].indexOf("---");
                    auth = auths[i].substring(0, index);
                    readOnly = Integer.valueOf(auths[i].substring(index + 3));
                    if (antPathMatcher.match(auth, requestUrl)) {//token有此权限
                        if ("GET".equals(method))//并且如果是GET操作的话直接放行
                            return chain.filter(exchange);
                        else {//如果不是get操作
                            if (readOnly != 1)//但拥有的权限不是只读权限
                                return chain.filter(exchange);//就可以直接放行
                        }
                    }
                }
            }
            if (transientAuth != null) {//判断是否有临时赋予的权利
                Set<Map.Entry<String, String[]>> trans = transientAuth.entrySet();
                for (Map.Entry<String, String[]> tran : trans) {
                    if (antPathMatcher.match(tran.getKey(), requestUrl))//token有此权限,直接放行
                        return chain.filter(exchange);
                }
            }
            //过滤该请求，不往下级服务去转发请求，到此结束
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            exchange.getResponse().writeWith(Flux.just("{\"result\":\"accessToken无此权限!\"}")
                    .map(bx -> exchange.getResponse().bufferFactory()
                            .wrap(bx.getBytes())));
            exchange.getResponse().getHeaders().setContentType(MediaType.parseMediaType("text/html;charset=UTF-8"));
            return chain.filter(exchange);
        } else {
            System.out.println("access_tocken无效");
            //过滤该请求，不往下级服务去转发请求，到此结束
            exchange.getResponse().setStatusCode(HttpStatus.SEE_OTHER);//重定向状态码
            exchange.getResponse().getHeaders().setLocation(URI.create("/training-web-service/login"));//重定向到登陆页面
            return exchange.getResponse().setComplete();
        }
    }

    @Override
    public int getOrder() {
        return -1;
    }

    //打印相关信息
    private void showDetails(ServerHttpRequest request, String access_token) {
        Consumer c = System.out::println;
        c.accept("access_tocken有效");
        String requestUrl = request.getURI().getPath();
        c.accept("请求的路径:".concat(requestUrl));
        c.accept("用户id:".concat(JwtUtil.getUserId(access_token)));
        String[] roles = JwtUtil.getRoles(access_token);
        c.accept("用户角色:");
        for (int i = 0, length = roles.length; i < length; i++) {
            c.accept(roles[i]);
        }
        c.accept("用户权限点:");
        String[] auths = JwtUtil.getAuth(access_token);
        for (int i = 0, length = auths.length; i < length; i++) {
            c.accept(auths[i]);
        }
        if (JwtUtil.getTransientAuth(access_token) != null)
            c.accept("用户暂时权限点:".concat(JwtUtil.getTransientAuth(access_token).toString()));
    }
}
